package edu.cmu.vlis.wassup.db;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;

import org.mybeans.dao.DAOException;
import org.mybeans.factory.BeanFactory;
import org.mybeans.factory.BeanFactoryException;
import org.mybeans.factory.BeanTable;
import org.mybeans.factory.MatchArg;
import org.mybeans.factory.RollbackException;
import org.mybeans.factory.Transaction;

import edu.cmu.vlis.wassup.databean.Event;
import edu.cmu.vlis.wassup.databean.EventTag;
import edu.cmu.vlis.wassup.databean.User;

/**
 * The class is used to access the database table Announcement
 * @author Jassica
 *
 */
public class UnprocessedEventDAO {
	
	private BeanFactory<Event> factory;
	
	public UnprocessedEventDAO() throws DAOException {
		try {					
			String jdbcDriverName 	= "com.mysql.jdbc.Driver";
			String jdbcURL			= "jdbc:mysql:///wassup?user=root&password=mysql";

			BeanTable.useJDBC(jdbcDriverName, jdbcURL);
			
			UserDAO userDAO = new UserDAO();
			
	        BeanTable<Event> eventTable = BeanTable.getInstance(
	        								Event.class,
	        								"unp_events", 
	        								userDAO.getFactory());
	        
	        if (!eventTable.exists()) 
	        	eventTable.create("hashId");
	        
	        // Long running web apps need to clean up idle database connections.
	        // So we can tell each BeanTable to clean them up.  (You would only notice
	        // a problem after leaving your web app running for several hours.)
	        eventTable.setIdleConnectionCleanup(true);
	
	        // Get a BeanFactory which the actions will use to read and write rows of the "user" table
	        factory = eventTable.getFactory();
		} catch (BeanFactoryException e) {
			throw new DAOException(e);
		}		
	}
	
	public UnprocessedEventDAO(String URL) throws DAOException {
		try {					
			String jdbcDriverName 	= "com.mysql.jdbc.Driver";
			String jdbcURL			= "jdbc:mysql://" + URL + "/wassup?user=root&password=mysql";

			BeanTable.useJDBC(jdbcDriverName, jdbcURL);
			
			UserDAO userDAO = new UserDAO();
			
	        BeanTable<Event> eventTable = BeanTable.getInstance(
	        								Event.class,
	        								"unp_events", 
	        								userDAO.getFactory());
	        
	        if (!eventTable.exists()) 
	        	eventTable.create("hashId");
	        
	        // Long running web apps need to clean up idle database connections.
	        // So we can tell each BeanTable to clean them up.  (You would only notice
	        // a problem after leaving your web app running for several hours.)
	        eventTable.setIdleConnectionCleanup(true);
	
	        // Get a BeanFactory which the actions will use to read and write rows of the "user" table
	        factory = eventTable.getFactory();
		} catch (BeanFactoryException e) {
			throw new DAOException(e);
		}		
	}
	
		
	/**
	 * To check whether the given announcement_id exists
	 * @param announcement_id
	 * @return
	 * @throws DAOkException 
	 */
	public boolean isEventExist(String eventId) {
		boolean result = false;
		
		Event event;
		try {
			event = factory.lookup(eventId);
			if (event != null) 
				result = true;
		} catch (RollbackException e) {
			e.printStackTrace();
		}
		
		return result;		
	}
	
	/**
	 * @param event
	 * @return
	 */
	private String createMessage(Event event) {
		String message = "";
		message += "Subject: "+event.getName();
		message += "; ";
		message += "Start Date: "+event.getStartDate();
		message += "; ";
		message += "Start Time: "+event.getStartTime();
		message += "; ";
		
		message += "End Date: ";
		if (event.getEndDate()==null || event.getEndDate().trim().equals("")) {
			message += event.getStartDate();
		}
		else message += event.getEndDate();
		message += "; ";
		
		message += "End Time: ";
		if (event.getEndTime()==null || event.getEndTime().trim().equals("")) {
			message += event.getStartTime();
		}
		else message += event.getEndTime();
		message += "; ";
		
		message += "Location: ";
		message += event.getStreet() + " " + event.getCity() + " "+event.getState()+" "+event.getUrl();
		message += "; ";
		
		message += "Description: ";
		if (event.getDescription()==null || event.getDescription().trim().equals(""))
			message += event.getName();
		else message += event.getDescription();
		message += "; ";
		
		if (event.getSender()!=null)
			message += "Sender: "+event.getSender().getUserName();
		
		message = message.trim();
		return message;
	}
	
	/** Lookup whether a typical announcement exists in the table
	 * Return Not Found if the announcements information is missing
	 * Otherwise return detailed information about announcement of a user
	 * 
	 * @param announcement_id
	 * @return
	 * @throws RollbackException
	 */
	public Event lookup(String eventId) throws DAOException {
		Event event = null;
		try {
			event = factory.lookup(eventId);
			System.out.println(event);
		} catch (RollbackException e) {
			e.printStackTrace();
			throw new DAOException(e);
		}		
		return event;
	}
	
		
	/**
	 * Get the hashvalue of the input message
	 * Stroe the hashvalue into the database to achieve primary key of the announcement
	 * @param message the user input clear-text message
	 * @return hash value of the input
	 */
	public String hash(String message) {
		MessageDigest md = null;
		try {
		  md = MessageDigest.getInstance("SHA1");
		} catch (NoSuchAlgorithmException e) {
		  throw new AssertionError("Can't find the SHA1 algorithm in the java.security package");
		}

		String saltString = String.valueOf(12345);
		
		md.update(saltString.getBytes());
		md.update(message.getBytes());
		byte[] digestBytes = md.digest();

		// Format the digest as a String
		StringBuffer digestSB = new StringBuffer();
		for (int i=0; i<digestBytes.length; i++) {
		  int lowNibble = digestBytes[i] & 0x0f;
		  int highNibble = (digestBytes[i]>>4) & 0x0f;
		  digestSB.append(Integer.toHexString(highNibble));
		  digestSB.append(Integer.toHexString(lowNibble));
		}
		String digestStr = digestSB.toString().trim();

		return digestStr;
	}
	
	
	
	/** 
	 * Insert a new tuple of announcement into the beanTable
	 * Returns error message if creation fails.
	 * Otherwise return a null upon success.
	 * 
	 * @param ann
	 * @param tags
	 * @return
	 * @throws RollbackException
	 */
	public String insert(Event event, String tag) throws RollbackException 
	{
		String message = createMessage(event);
		
		String hashId = hash(message);
		event.setHashId(hashId);		
		
		if (!Transaction.isActive())	// If the former transaction does not end normally
			Transaction.begin();	// Begin a new transaction
		
		Event dbEvent = factory.lookup(hashId);
				
		if (dbEvent==null) {
			dbEvent = factory.create(hashId);
			factory.copyInto(event, dbEvent);
			
			
		if (event.getEndDate()==null || event.getEndDate().trim().equals("")) {
			dbEvent.setEndDate(event.getStartDate());
		}
		if (event.getEndTime()==null || event.getEndTime().trim().equals("")) {
			dbEvent.setEndTime(event.getStartTime());
		}
		if (event.getDescription()==null || event.getDescription().trim().equals(""))
			dbEvent.setDescription(event.getName());
			
			
		Transaction.commit();
		System.out.println("Event inserted successfully " + event);
		}
		
		// insert tags
		UnprocessedEventTagDAO unprocessedEventTagDAO = null;
		try {
			unprocessedEventTagDAO = new UnprocessedEventTagDAO();
		} catch (DAOException e) {
			e.printStackTrace();
			throw new RollbackException(e);
		}
		
		try {
			unprocessedEventTagDAO.insert(dbEvent, tag);
		} catch (DAOException e) {
			e.printStackTrace();
			try {
				delete(dbEvent.getHashId());
			} catch (DAOException e1) {
				e1.printStackTrace();
				throw new RollbackException(e);
			}
			throw new RollbackException(e);
		}
		
		
		
//		UnprocessedAnnouncement_Tag_Dao tagmanager = new UnprocessedAnnouncement_Tag_Dao();
//		try {
//			for (int i=0; i<tags.size(); i++)
//				tagmanager.insert(announcement, tags.get(i)); 
//		}
//		catch (RollbackException e) { // if exception happens on insertion on the tag table
//			delete(ann.getAnnouncement_id());	// delete the record in the announcement table
//			throw new RollbackException("Announcement inserts successfully, while tag fails");
//		}
		
		return dbEvent.getHashId();
	}
	
	
	
	/** Update an announcement record in the table
	 * Insert new tuple in the announcement table if the record does not exist
	 * Update the new tuple information 
	 * 
	 * @param announcement_id
	 * @param ann
	 * @param tags
	 * @return
	 * @throws RollbackException
	 */
	public Event update(String announcement_id, Event ann, List<String> tags) throws DAOException {
		Event event = null;
		if (!Transaction.isActive())
			try {
				Transaction.begin();
				event = factory.lookup(announcement_id);
				if (event != null) {
					factory.copyInto(ann, event);
					Transaction.commit();		
					
					UnprocessedEventTagDAO unprocessedEventTagDAO = new UnprocessedEventTagDAO();
					try {
						for (int i=0; i<tags.size(); i++)
							unprocessedEventTagDAO.insert(event, tags.get(i)); 
					} catch (DAOException e) { 
						// if exception happens on insertion on the tag table
						delete(ann.getHashId());	// delete the record in the announcement table
						throw new DAOException("Announcement inserts successfully, while tag fails");
					}
					
					
//					UnprocessedEventTagDAO tagmanager = new UnprocessedEventTagDAO();
//					try {
//						for (int i=0; i<tags.size(); i++)
//							tagmanager.insert(announcement, tags.get(i)); 
//					}
//					catch (RollbackException e) { // if exception happens on insertion on the tag table
//						delete(ann.getHashId());	// delete the record in the announcement table
//						throw new RollbackException("Announcement inserts successfully, while tag fails");
//					}					
				}				
			} catch (RollbackException e) {
				e.printStackTrace();
				throw new DAOException(e);
			}			
			return event;		
	}
	
	/** Delete a tuple with primary key as announcement_id from announcement table
	 * return null if no tuple is affected
	 * 
	 * @param announcement_id
	 * @return
	 * @throws RollbackException
	 */
	public void delete(String eventId) throws DAOException {
		Event event = null;
		try {
			event = factory.lookup(eventId);
		} catch (RollbackException e) {
			
			e.printStackTrace();
		}
		
		if (event != null) {
			
			// first delete event tags record for this event
			UnprocessedEventTagDAO unprocessedEventTagDAO = new UnprocessedEventTagDAO();
			EventTag[] eventTags = unprocessedEventTagDAO.getEventTagsForEvent(event);
			for (EventTag eventTag : eventTags) {
				unprocessedEventTagDAO.delete(eventTag.getEventId(), eventTag.getTag());
			}
			
			try {
				if (!Transaction.isActive())				
					Transaction.begin();
				
				factory.delete(eventId);
				
				Transaction.commit();
			} catch (RollbackException e) {					
				e.printStackTrace();
			}			
		}		
	}
	
	/**
	 * Get all the entries in one table
	 * @return
	 * @throws RollbackException
	 */
	public Event[] getAll() {
		Event[] allEvents = null;
		try {
			allEvents = factory.match();
		} catch (RollbackException e) {
			e.printStackTrace();
		}
		return allEvents;
	}
	
	/**
	 * Get all the announcements according to one user
	 * @param args
	 * @throws RollbackException 
	 * @throws RollbackException
	 */
	public Event[] getFromSender(User me){
		Event[] allEventsFromMe = null;
		try {
			allEventsFromMe = factory.match(MatchArg.equals("sender", me));
		} catch (RollbackException e) {
			e.printStackTrace();
		}
		return allEventsFromMe;	
	}
	
	public BeanFactory<Event> getFactory() {
		return factory;
	}
	
	public static void main(String[] args) throws RollbackException {
		
	}
}
